﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;

namespace Expression表达式目录树
{
    /// <summary>
    /// 泛型方法推展
    /// 当我们使用静态方法，会执行静态的无参构造函数 ，不会调用无参构造函数
    /// 我们使用泛型的时候会保存不同泛型的副本，一直保存在内存里面不会释放，所以可以
    /// 实现伪硬编码
    /// </summary>
    public class GenericityExpand<T, TOut>
    {
        private static Func<T, TOut> _Func = null;
        static GenericityExpand()
        {
            //首先我们还是创建一个参数
            ParameterExpression parameter = Expression.Parameter(typeof(T));
            //获取要转化后的类型
            Type type = typeof(TOut);
            //创建一个容器存放解析的成员
            List<MemberBinding> list = new List<MemberBinding>();
            //遍历属性
            foreach (var item in type.GetProperties())
            {
                //获取参数中item.Name对应的名称
                MemberExpression memberExpression = Expression.Property(parameter, typeof(T).GetProperty(item.Name));
                //判断是否存在
                if (memberExpression != null)
                {
                    MemberBinding member = Expression.Bind(item, memberExpression);
                    list.Add(member);
                }
            }
            //遍历字段
            foreach (var item in type.GetFields())
            {
                //获取参数中item.Name对应的名称
                MemberExpression memberExpression = Expression.Field(parameter, typeof(T).GetField(item.Name));
                //判断是否存在
                if (memberExpression != null)
                {
                    MemberBinding member = Expression.Bind(item, memberExpression);
                    list.Add(member);
                }
            }
            //初始化转换后的类型，并且进行初始化赋值
            MemberInitExpression memberInit = Expression.MemberInit(Expression.New(typeof(TOut)), list);
            //所有的准备工作已经完成准备生成lambda
            Expression<Func<T, TOut>> expression = Expression.Lambda<Func<T, TOut>>(memberInit, new ParameterExpression[] {
                parameter
                });
            //生成委托存放到我们的泛型委托中
            _Func = expression.Compile();
        }

        public static TOut ToObj(T obj)
        {
            return _Func(obj);
        }
    }
}
